<?php
/***********************************************************************
 * @package    IvanEngine
 * @subpackage Core
 * @author     Ivan <ivanzx@msn.com>
 * @created    2010-05-23
 ***********************************************************************/
if(defined("IVANENGINE_FRAMEWORK_PLUGIN"))
    die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_FRAMEWORK_PLUGIN", true);

require_once (dirname(__FILE__).'/Interface/IPlugin.php');
require_once (dirname(__FILE__).'/Helper/Helper.php');
require_once (dirname(__FILE__).'/MVC/Action.php');
require_once (dirname(__FILE__).'/MVC/View.php');

abstract class CPlugin extends CObject implements IPlugin
{
    protected   
        $m_pluginMgr    = null,
        $m_view         = null,
        $m_action       = null,
        $m_path         = null,
        $m_varMap       = null,
        $m_xml          = null,
        $m_i18n         = null,
        $m_internal     = false,
        
        // plugin xml config variable
        $m_nick         = null,
        $m_version      = null,
        $m_auther       = null,
        $m_description  = null,
        $m_secure       = null,
        $m_defSecure    = false;
    
    public function __construct($pluginName, $isInternalPlugin = false)
    {
        parent::__construct($pluginName);
        $this->m_pluginMgr  = CPluginManager::Get();
        $rootWinName        = CWindowManager::Get()->GetRoot()->GetName();
        $this->m_view       = new CView($rootWinName);
        $this->m_internal   = $isInternalPlugin;
        $this->m_path       = CPluginManager::GetPluginPath($pluginName, $this->m_internal);
        $this->m_varMap     = new CMap();
        $this->m_xml        = new CXml();
        
        // construction action class
        $actionClass        = $this->GetActionClassName($pluginName);
        $this->m_action     = new $actionClass($this);
        
        // Load xml data from cache file
        $callback           = new CCacheCallback(
            '_LoadPluginConfigData', $this->m_pluginMgr, array($this));
        $xmlFile            = $this->m_path.self::GetXmlFile();
        $cacheFile          = CCacheManager::Get()->
            GetCacheFilePath('Plugins'.M_DS.$this->GetName().M_DS.'Plugin.dat');
        CCacheManager::Get()->AddCacheFile($xmlFile, $callback, $cacheFile);
        
        // load the plugin language package
        $packagePath    = $this->m_path.M_DS.CI18NManager::TAG;
        $packageCache   = CCacheManager::Get()->
            GetCacheFilePath('Plugins'.M_DS.$pluginName.M_DS.CI18NManager::TAG);
        CI18NManager::Get()->AddPackage($packagePath, true, $packageCache);
        
        // get i18n package and push in to array
        $this->m_i18n       = new CMap();
        foreach( CI18NManager::Get()->GetStandard() as $key => $value ){
            $this->m_i18n->Add($key, $value->GetContent());
        }
        foreach( CI18NManager::Get()->GetCommon() as $key => $value ){
            $this->m_i18n->Add($key, $value->GetContent());
        }
        foreach( CI18NManager::Get()->GetCustom() as $key => $value ){
            $this->m_i18n->Add($key, $value->GetContent());
        }
        $this->Set('Language', $this->m_i18n->GetArray());
        $this->Set('_L', $this->m_i18n->GetArray());
        
        $this->m_action->_Initialize();
    }
    
    public function __destruct()
    {
        unset($this->m_view);
        unset($this->m_varMap);
        unset($this->m_action);
        unset($this->m_xml);
        parent::__destruct();
    }
    
    public function GetPath()
    {
        return $this->m_path;
    }
    
    public function SetView(CView & $view)
    {
        $oldContent     = $this->m_view->GetContent();
        $newContent     = $view->GetContent();
        $this->m_view   = null;
        $this->m_view   = $view;
        $this->m_view->SetContent($oldContent);
        $this->m_view->AddContent($newContent);
    }
    
    public function GetView()
    {
        return $this->m_view;
    }
    
    public function GetI18N()
    {
        return $this->m_i18n;
    }
    
    public function Set($key, $val)
    {
        if( $this->m_varMap->Has($key) )
        {
            $this->m_varMap->Set($key, $val);
        }else{
            $this->m_varMap->Add($key, $val);
        }
    }
    
    public function Get($key)
    {
        return $this->m_varMap->Get($key);
    }
    
    public function Remove($key)
    {
        $this->m_varMap->Remove($key);
    }
    
    public function Render(CWindow & $win, $isHtml = false, $isAppend = false)
    {
        $win->Render($this->m_view, $isHtml, $isAppend);
    }
    
    public function ExecuteAction($actionName, CRequest & $request)
    {
        CLog::Get()->Note('Execute action: '.$this->GetName().'::'.$actionName);
        
        $request->AddRequestParameters(array(
            'Plugin' => $this->GetName(), 'Action' => $actionName));
            
        $securePluginName   = CConfig::Option(CConfig::OT_SecurePlugin);
        $secureActionName   = CConfig::Option(CConfig::OT_SecureAction);
        if( $this->_IsSecure($actionName) && 
            $this->GetName() !=  $securePluginName &&
            $actionName !=  $secureActionName)
        {
            if( !CContext::Get()->GetUser()->IsAuthenticated() )
            {
                CLog::Get()->Note($this->GetName().'::'.$actionName.
                    ' is secure, so forword to login page.');
                
                $request->AddRequestParameters(array(
                    'LastPlugin' => $this->GetName(), 'LastAction' => $actionName));
                CContext::Get()->GetController()->Forward(
                    $securePluginName, $secureActionName);
                    
                CContext::Get()->GetEngine()->Shutdown();
            }
        }
        
        if( $this->m_action->PreExecuteAction($actionName, $request) === false ){
            return false;
        }
        
        CCacheManager::Get()->AddCacheFile(
            $this->m_path.M_DS.'Windows'.M_DS.$actionName.'.Window.xml', 
            new CCacheCallback('_LoadWindows', $this),
            CCacheManager::Get()->GetCacheFilePath('Plugins'.M_DS.
                $this->GetName().M_DS.'windows'.M_DS.$actionName.'.Window.dat'), true);
        
        $funReturn = call_user_func(
            array($this->m_action, $this->GetActionName($actionName)), $request);
        
        if( $this->m_action->PostExecuteAction($actionName, $request) === false ){
            return false;
        }
        
        if( $funReturn !== null && !$funReturn ){
            return $funReturn;
        }
        
        $layoutContent  = '';
        $actionContent  = '';
        $actionViewFile = $this->GetPath().M_DS.'View'.M_DS.self::GetActionViewName($actionName);
        if( CFileHelper::IsExsit($actionViewFile) )
        {
            $varArray   = array();
            foreach($this->m_action->_GetVarMap() as $key => $val )
                $varArray[$key] = $val;
            extract($varArray);
            ob_start();
            ob_implicit_flush(0);
            require($actionViewFile);
            $actionContent  = ob_get_clean();
        }
        if( $this->m_action->GetLayout() )
        {
            $varArray   = array();
            foreach($this->m_varMap as $key => $val )
                $varArray[$key] = $val;
            $varArray['ActionContent'] = $actionContent;
            extract($varArray);
            ob_start();
            ob_implicit_flush(0);
            require($this->m_action->GetLayout());
            $layoutContent  = ob_get_clean();
        }else{
            $layoutContent  = $actionContent;
        }
        
        $this->m_view->AddContent($layoutContent);
        
        $renderResult = $this->m_action->PreRender($actionName, $request);
        if($renderResult !== null && !$renderResult){
            return $funReturn;
        }
        
        $this->m_view->Render();
        
        $renderResult = $this->m_action->PostRender($actionName, $request);
        if($renderResult !== null && !$renderResult){
            return $funReturn;
        }
        
        $this->m_view->Clear();
        
        return $funReturn;
    }
    
    static public function GetActionName($actionName)
    {
        return 'Do'.ucfirst($actionName);
    }
    
    static public function GetActionViewName($actionName)
    {
        return $actionName.'.View.php';
    }
    
    public function _LoadWindows($isGenerate, $origin, $cache)
    {
        $winMgr = CWindowManager::Get();
        $winRoot= null;
        if( $isGenerate ){
            $xml    = new CXml(CXml::NST_ATTR, CWindow::WA_NAME);
            $xml->Load($origin);
            $rootNode   = $xml->GetRoot();
            $winRoot    = new CWindow($winMgr->GetRoot()->GetName());
            
            // check mode, clear all windows then it is relace
            $strMode= $rootNode->GetAttribute('mode');
            $mode   = 1;
            if( !empty($strMode) && strtolower($strMode) == 'new' ){
                $mode   = 2;
                $jsCode = $winMgr->Clear();
                $this->m_view->AddScript($jsCode, true);
            }
            $winRoot->FromXmlNode($rootNode);
        }else{
            $data   = CFileHelper::Read($cache);
            list($mode, $winRoot)= unserialize($data);
            if($mode == 2){
                // clear all window use javascript
                $jsCode = $winMgr->Clear();
                $this->m_view->AddScript($jsCode, true);
            }
        }
        
        foreach( $winRoot as $name => $child )
        {
            if( !$winMgr->GetRoot()->GetChilds()->HasKey($name) )
            {
                $child->SetParent($winMgr->GetRoot());
                $winMgr->GetRoot()->AddChild($child);
            }
            if( !$winMgr->HasInStorage($name) )
            {
                // add new window to stage use javascript
                $this->m_view->AddScript($child->Render(false), true);
            }else{
                $modifier = new CWindowModifier($child->GetName(), $this->m_view);
                $modifier->UpdateAll();
                $modifier->Set(CWindow::WA_VISIBLE, true);
                $modifier->Execute();
                unset($modifier);
            }
        }
        if( $isGenerate ){
            CFileHelper::Write($cache, serialize(array($mode, $winRoot)));
        }else{
            $winRoot->AddChildToManager();
        }
    }

    public function _LoadXmlData($isGenerate, $origin, $cache)
    {
        if( $isGenerate ){
            $this->m_xml->Load($origin);
            $content    = $this->m_xml->serialize();
            CFileHelper::Write($cache, $content);
        }else{
            $content = CFileHelper::Read($cache);
            $this->m_xml->unserialize($content);
        }
        $this->_LoadConfigFromXml();
    }
    
    protected function _LoadConfigFromXml()
    {
        $nick       = $this->m_xml->GetRoot()->GetChild('nick');
        $auther     = $this->m_xml->GetRoot()->GetChild('auther');
        $version    = $this->m_xml->GetRoot()->GetChild('version');
        $description= $this->m_xml->GetRoot()->GetChild('description');
        $secure     = $this->m_xml->GetRoot()->GetChild('secure');
    
        $errorNodeName  = null;
        if( !$nick ){
            $errorNodeName  = 'nick';
        }else if( !$auther ){
            $errorNodeName  = 'auther';
        }else if( !$version ){
            $errorNodeName  = 'version';
        }else if( !$description ){
            $errorNodeName  = 'description';
        }else if( !$secure ){
            $errorNodeName  = 'secure';
        }
        if( $errorNodeName )
        {
            throw new CException('This directory plugin XML file fall "'.$errorNodeName.'" node:<br>'.
                CPluginManager::GetPluginFile($this->GetName()));
        }
        $this->m_nick       = $nick->GetContent();
        $this->m_auther     = $auther->GetContent();
        $this->m_version    = $version->GetContent();
        $this->m_description= $description->GetContent();
        $this->m_defSecure  = CStringHelper::ToBool($secure->GetAttribute('default'));
        if( $this->m_defSecure === null ){
            $this->m_defSecure  = false;
        }
        if( $secure->GetContent() )
        { 
            $this->m_secure     = new CVector();
            $secures            = explode(',', $secure->GetContent());
            foreach( $secures as $action ){
                $action = ucfirst($action);
                if( !method_exists($this->m_action, $this->GetActionName($action)) )
                {
                    throw new CExceptionInvalidArgument(
                        'can not find out \''.$action.'\'action in secure list.');
                }
                $this->m_secure->Add($action);
            }
        }
    }
    
    protected function _IsSecure($actionName)
    {
        if( $this->m_secure && $this->m_secure->Has($actionName) ){
            return true;
        }
        if( $this->m_defSecure ){
            return true;
        }
        return false;
    }
    
        static public function GetPluginFile()
    {
        return 'Plugin.php';
    }
    
    static public function GetXmlFile()
    {
        return 'Plugin.xml';
    }
    
    static public function GetActionFile()
    {
        return 'Action.php';
    }
    
    static public function GetLayoutFile()
    {
        return 'Layout.php';
    }
    
    static public function GetPluginClassName($pluginName)
    {
        return 'CPlugin'.ucfirst($pluginName);
    }
    
    static public function GetActionClassName($pluginName)
    {
        return 'CAction'.ucfirst($pluginName);
    }
    
    public function __call($method, $arguments)
    {
        if( substr($method, 0, 2) == 'Do' )
        {
            throw new CException404Error(sprintf('Action "%s/%s" does not exist.', 
                $this->GetName(), substr($method, 2)));
        }else{
            parent::__call($method, $arguments);
        }
    }
}

?>
